package com.snap.vseries.serial;

import com.snap.vseries.AppConfig;
import com.snap.vseries.bean.ProgramSection;
import com.snap.vseries.bean.ProgramStage;

import java.util.ArrayList;
import java.util.List;


/**
 * cmd[2] 设备号
 * cmd[3] cmd[4] 帧长
 * cmd[5] 帧类型
 * cmd[6] 功能码
 * <p>
 * 最短8个字节（假设只有功能码 不含数据）
 */
public class Cmd {

    /*-------------------------------------------设置模板应答--------------------------------------------------------------*/

    /**
     * 通用协议的前5个字节填充
     *
     * @param cmd         命令，长度确定好
     * @param deviceType  设备类型
     * @param moduleIndex 模块号
     * @return
     */
    private static byte[] fillNormal(byte[] cmd, int deviceType, int moduleIndex) {
        cmd[0] = AppConfig.CMD_START1;
        cmd[1] = AppConfig.CMD_START2;
        cmd[2] = (byte) deviceType;
        cmd[3] = (byte) moduleIndex;
        cmd[4] = (byte) (cmd.length - 2);
        return cmd;
    }

    //帧头 设备地址 帧长(2字节) 帧类型 数据信息（长度可变） 校验
    private static byte[] fill(int deviceId, byte[] data) {
        byte[] cmd = new byte[data.length + 6];
        cmd[0] = (byte) 0xa5;
        cmd[1] = (byte) 0x5a;
        cmd[2] = (byte) (deviceId+1);
        int length = cmd.length - 2;
        cmd[3] = (byte) ((length & 0xff00) >> 8);
        cmd[4] = (byte) (length & 0xff);
        System.arraycopy(data, 0, cmd, 5, data.length);
        cmd[cmd.length - 1] = getParity(cmd);
        return cmd;
    }

    /**
     * 验证命令是否符合协议
     */
    public static boolean cmdValidate(byte[] cmd) {
        if (cmd == null) return false;
        if (cmd.length < 8) return false;
        if (cmd[0] != AppConfig.CMD_START1) return false;
        if (cmd[1] != AppConfig.CMD_START2) return false;
        int frameLength = ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff);
        if (frameLength != cmd.length - 2) return false;
        return cmd[cmd.length - 1] == getParity(cmd);
    }

    /**
     * 是否包含大于1条指令
     */
    public static boolean isOneMore(byte[] cmd) {
        return cmd != null
                && cmd.length > 8
                && cmd[0] == AppConfig.CMD_START1
                && cmd[1] == AppConfig.CMD_START2
                && cmd.length > ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff) + 2;
    }

    /**
     * 获取校验位
     */
    private static byte getParity(byte[] cmd) {
        byte parity = 0x00;
        for (int i = 0; i < cmd.length - 1; i++) {
            parity += (cmd[i] & 0xff);
        }
        return parity;
    }

    public static byte[] pcrProgram(int deviceId, List<ProgramStage> stages) {
        byte frameType = 0x00;
        byte functionCode = 0x00;
        byte stageSize = (byte) stages.size();
        List<Byte> byteList = new ArrayList<>();
        byteList.add(frameType);
        byteList.add(functionCode);
        byteList.add(stageSize);
        for (int i = 0; i < stages.size(); i++) {
            List<ProgramSection> sections = stages.get(i).getSections();
            int sectionSize = sections == null ? 0 : sections.size();//size=0 一般不会发生，页面会进行控制
            byteList.add((byte) sectionSize);
            byteList.add((byte) stages.get(i).getCycleCount());
            for (int j = 0; j < sectionSize; j++) {
                int temp = (int) (sections.get(j).getTemp() * 100);
                byteList.add((byte) ((temp & 0xff00) >> 8));
                byteList.add((byte) (temp & 0xff));
                int duration = sections.get(j).getDuration();
                byteList.add((byte) ((duration & 0xff00) >> 8));
                byteList.add((byte) (duration & 0xff));
                boolean light = sections.get(j).isLighting();
                byteList.add((byte) (light ? 1 : 0));
            }
        }
        byte[] data = new byte[byteList.size()];
        for (int i = 0; i < data.length; i++) {
            data[i] = byteList.get(i);
        }
        return fill(deviceId, data);
    }

    public static byte[] pcrOperation(int deviceId, boolean start) {
        byte[] data = new byte[3];
        data[0] = 0;//控制帧0
        data[1] = 1;//功能码
        data[2] = (byte) (start ? 0 : 1);
        return fill(deviceId, data);
    }

    /**
     * 设置PID
     */
    public static byte[] setPid(int deviceId, float[] params) {
        if (params.length != 12) {
            throw new RuntimeException();
        }
        byte[] data = new byte[26];
        data[0] = 0;//控制帧0
        data[1] = 2;//命令码
        for (int i = 0; i < params.length; i++) {
            int value = (int) (params[i] * 100);
            data[i * 2 + 2] = (byte) ((value & 0xff00) >> 8);
            data[i * 2 + 3] = (byte) (value & 0xff);
        }
        return fill(deviceId, data);
    }

    /**
     * 设置温漂
     */
    public static byte[] setTempDrift(int deviceId, float[] params) {
        if (params.length != 4) {
            throw new RuntimeException();
        }
        byte[] data = new byte[10];
        data[0] = 0;//控制帧0
        data[1] = 3;
        for (int i = 0; i < params.length; i++) {
            int value = (int) (params[i] * 100);
            data[i * 2 + 2] = (byte) ((value & 0xff00) >> 8);
            data[i * 2 + 3] = (byte) (value & 0xff);
        }
        return fill(deviceId, data);
    }

    /**
     * 查询设备状态信息
     * 其中组合查询fe举例：设备运行状态 00H + 温度状态 01H + 实时荧光数据 02H ，帧数据如下：
     * 帧头（A5 5A）+设备地址（01）+帧长（00 0A）+帧类型（02）+查询码（FE）+组合数n(1 byte) + 查询码1(1 byte) + 查询码2(1 byte) + ... + 查询码n(1 byte)
     * A5 5A 01 00 0A 02 FE 03 00 01 02 10
     */
    public static byte[] query(int deviceId, int... type) {
        if (type.length == 0) {
            throw new RuntimeException();
        } else if (type.length == 1) {
            //单个查询状态信息
            byte[] data = new byte[2];
            //帧类型，2为查询帧
            data[0] = 2;
            data[1] = (byte) type[0];
            return fill(deviceId, data);
        } else {
            //组合查询状态信息
            byte[] data = new byte[type.length + 3];
            //查询帧
            data[0] = 2;
            //查询码，fe为组合查询设备信息，FF为查询全部设备信息
            data[1] = (byte) 0xfe;
            data[2] = (byte) type.length;
            for (int i = 0; i < type.length; i++) {
                data[i + 3] = (byte) type[i];
            }
            return fill(deviceId, data);
        }
    }

    /**
     * 查询温漂
     */
    public static byte[] queryTempDrift(int deviceId) {
        byte[] data = new byte[2];
        data[0] = 2;//查询帧2
        data[1] = 4;//查询码4，查询温漂
        return fill(deviceId, data);
    }

    public static byte[] queryPID(int deviceId) {
        byte[] data = new byte[2];
        data[0] = 2;//查询帧2
        data[1] = 3;//查询码3
        return fill(deviceId, data);
    }


    /**
     * 风扇开关
     *
     * @param turnOn true打开电源 false关闭电源
     */
    public static byte[] fan(int deviceType, int moduleIndex, boolean turnOn) {
        int cmdLength = 8;
        byte[] cmd = fillNormal(new byte[cmdLength], deviceType, moduleIndex);
        cmd[5] = 0x02;
        cmd[6] = (byte) (turnOn ? 0x01 : 0x00);
        cmd[cmdLength - 1] = getParity(cmd);
//        Logger.v("fan:" + HexUtils.ByteArrToHex(cmd));
        return cmd;
    }


    /**
     * 查询采光状态
     */
    public static byte[] getLightingState(int deviceType, int moduleIndex) {
        int cmdLength = 7;
        byte[] cmd = fillNormal(new byte[cmdLength], deviceType, moduleIndex);
        cmd[5] = 0x32;
        cmd[cmdLength - 1] = getParity(cmd);
//        Logger.v("light state:" + HexUtils.ByteArrToHex(cmd));
        return cmd;
    }
}